微信支付 您所在的位置:网站首页 微信生成付款码 文档  微信支付

 微信支付

2023-04-01 04:37| 来源: 网络整理| 查看: 265

1 开发准备 1.1开发文档

微信支付接口调用的整体思路:

按API要求组装参数,以XML方式发送(POST)给微信支付接口(URL),微信支付接口也是以XML方式给予响应。程序根据返回的结果(其中包括支付URL)生成二维码或判断订单状态。

在线微信支付开发文档:

https://pay.weixin.qq.com/wiki/doc/api/index.html

2 支付流程分析 2.1 订单支付分析

如上图,步骤分析如下:

用户下单之后,订单数据会存入到MySQL中 用户下单后,进入支付页面,支付页面调用支付系统,从微信支付获取二维码数据,并在页面生成支付二维码。 用户扫码支付后,微信支付服务器会通调用前预留的回调地址,并携带支付状态信息。 支付系统接到支付状态信息后,将支付状态信息发送给RabbitMQ 订单系统监听RabbitMQ中的消息获取支付状态,并根据支付状态修改订单状态 为了防止网络问题导致notifyurl没有接到对应数据,延迟队列,定时更新对应状态。 3 二维码创建(了解)

今天主要讲微信支付,后面为了看到效果,我们简单说下利用qrious制作二维码插件。

qrious是一款基于HTML5 Canvas的纯JS二维码生成插件。通过qrious.js可以快速生成各种二维码,你可以控制二维码的尺寸颜色,还可以将生成的二维码进行Base64编码。

qrious.js二维码插件的可用配置参数如下:

下面的代码即可生成一张二维码

二维码入门小demo var qr = new QRious({ element:document.getElementById('qrious'), size:250, level:'H', value:'http://www.ekgc.com' });

运行效果:

4 微信扫码支付简介 4.1 微信扫码支付申请

第一步:注册公众号(类型须为:服务号)

请根据营业执照类型选择以下主体注册:个体工商户| 企业/公司| 政府| 媒体| 其他类型。

第二步:认证公众号

公众号认证后才可申请微信支付,认证费:300元/次。

第三步:提交资料申请微信支付

登录公众平台,点击左侧菜单【微信支付】,开始填写资料等待审核,审核时间为1-5个工作日内。

第四步:开户成功,登录商户平台进行验证

资料审核通过后,请登录联系人邮箱查收商户号和密码,并登录商户平台填写财付通备付金打的小额资金数额,完成账户验证。

第五步:在线签署协议

本协议为线上电子协议,签署后方可进行交易及资金结算,签署完立即生效。

如果申请不下来可以关注我,给我私信可以提供微信支付账号,无需申请。

我们在本文章中会用到”统一下单”和”查询订单”两组API

5 微信支付模式介绍 5.1 模式一

业务流程说明:

商户后台系统根据微信支付规定格式生成二维码(规则见下文),展示给用户扫码。 用户打开微信“扫一扫”扫描二维码,微信客户端将扫码内容发送到微信支付系统。 微信支付系统收到客户端请求,发起对商户后台系统支付回调URL的调用。调用请求将带productid和用户的openid等参数,并要求商户系统返回交数据包,详细请见"本节3.1回调数据输入参数" 商户后台系统收到微信支付系统的回调请求,根据productid生成商户系统的订单。 商户系统调用微信支付【统一下单API】请求下单,获取交易会话标识(prepay_id) 微信支付系统根据商户系统的请求生成预支付交易,并返回交易会话标识(prepay_id)。 商户后台系统得到交易会话标识prepay_id(2小时内有效)。 商户后台系统将prepay_id返回给微信支付系统。返回数据见本节3.2回调数据输出参数 微信支付系统根据交易会话标识,发起用户端授权支付流程。 用户在微信客户端输入密码,确认支付后,微信客户端提交支付授权。 微信支付系统验证后扣款,完成支付交易。 微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。 微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。 未收到支付通知的情况,商户后台系统调用【查询订单API】。 商户确认订单已支付后给用户发货。

5.2 模式二

业务流程说明:

商户后台系统根据用户选购的商品生成订单。 用户确认支付后调用微信支付【统一下单API】生成预支付交易; 微信支付系统收到请求后生成预支付交易单,并返回交易会话的二维码链接code_url。 商户后台系统根据返回的code_url生成二维码。 用户打开微信“扫一扫”扫描二维码,微信客户端将扫码内容发送到微信支付系统。 微信支付系统收到客户端请求,验证链接有效性后发起用户支付,要求用户授权。 用户在微信客户端输入密码,确认支付后,微信客户端提交授权。 微信支付系统根据用户授权完成支付交易。 微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。 微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。 未收到支付通知的情况,商户后台系统调用【查询订单API】。 商户确认订单已支付后给用户发货。 6 微信支付SDK

微信支付提供了SDK, 大家下载后打开源码,install到本地仓库。

为了方便微信支付开发,我们可以在项目工程下引入依赖

com.github.wxpay wxpay-sdk     0.0.3

我们主要会用到微信支付SDK的以下功能:

1.获取随机字符串

WXPayUtil.generateNonceStr()

2.MAP转换为XML字符串(自动添加签名)

WXPayUtil.generateSignedXml(param, partnerkey)

3.XML字符串转换为MAP

WXPayUtil.xmlToMap(result) 7 HttpClient工具类

HttpClient是Apache Jakarta Common下的子项目,用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包,并且它支持HTTP协议最新的版本和建议。HttpClient已经应用在很多的项目中,比如Apache Jakarta上很著名的另外两个开源项目Cactus和HTMLUnit都使用了HttpClient。

HttpClient通俗的讲就是模拟了浏览器的行为,如果我们需要在后端向某一地址提交数据获取结果,就可以使用HttpClient.

关于HttpClient(原生)具体的使用不属于我们的学习内容,我们这里这里为了简化HttpClient的使用,提供了工具类HttpClient(对原生HttpClient进行了封装)

HttpClient依赖

org.apache.httpcomponents     httpclient

HttpClient工具类代码

import org.apache.http.Consts; import org.apache.http.HttpEntity; import org.apache.http.NameValuePair; import org.apache.http.ParseException; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.clienthods.*; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.TrustStrategy; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicNameValuePair; import org.apache.http.ssl.SSLContextBuilder; import org.apache.http.util.EntityUtils; import javax.net.ssl.SSLContext; import java.io.IOException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; public class HttpClient { private String url; private Map param; private int statusCode; private String content; private String xmlParam; private boolean isHttps; public boolean isHttps() { return isHttps; } public void setHttps(boolean isHttps) { this.isHttps = isHttps; } public String getXmlParam() { return xmlParam; } public void setXmlParam(String xmlParam) { this.xmlParam = xmlParam; } public HttpClient(String url, Map param) { this.url = url; this.param = param; } public HttpClient(String url) { this.url = url; } public void setParameter(Map map) { param = map; } public void addParameter(String key, String value) { if (param == null) param = new HashMap(); param.put(key, value); } public void post() throws ClientProtocolException, IOException { HttpPost http = new HttpPost(url); setEntity(http); execute(http); } public void put() throws ClientProtocolException, IOException { HttpPut http = new HttpPut(url); setEntity(http); execute(http); } public void get() throws ClientProtocolException, IOException { if (param != null) { StringBuilder url = new StringBuilder(this.url); boolean isFirst = true; for (String key : param.keySet()) { if (isFirst) { url.append("?"); }else { url.append("&"); } url.append(key).append("=").append(param.get(key)); } this.url = url.toString(); } HttpGet http = new HttpGet(url); execute(http); } /** * set http post,put param */ private void setEntity(HttpEntityEnclosingRequestBase http) { if (param != null) { List nvps = new LinkedList(); for (String key : param.keySet()) { nvps.add(new BasicNameValuePair(key, param.get(key))); // 参数 } http.setEntity(new UrlEncodedFormEntity(nvps, Consts.UTF_8)); // 设置参数 } if (xmlParam != null) { http.setEntity(new StringEntity(xmlParam, Consts.UTF_8)); } } private void execute(HttpUriRequest http) throws ClientProtocolException, IOException { CloseableHttpClient httpClient = null; try { if (isHttps) { SSLContext sslContext = new SSLContextBuilder() .loadTrustMaterial(null, new TrustStrategy() { // 信任所有 @Override public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { return true; } }).build(); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( sslContext); httpClient = HttpClients.custom().setSSLSocketFactory(sslsf) .build(); } else { httpClient = HttpClients.createDefault(); } CloseableHttpResponse response = httpClient.execute(http); try { if (response != null) { if (response.getStatusLine() != null) { statusCode = response.getStatusLine().getStatusCode(); } HttpEntity entity = response.getEntity(); // 响应内容 content = EntityUtils.toString(entity, Consts.UTF_8); } } finally { response.close(); } } catch (Exception e) { e.printStackTrace(); } finally { httpClient.close(); } } public int getStatusCode() { return statusCode; } public String getContent() throws ParseException, IOException { return content; } } 8 支付微服务搭建

创建shop.pay

创建application.yml,配置文件如下:

server: port: 1700 spring: application: name: pay-server cloud: nacos: discovery: server-addr: 127.0.0.1:8848 rabbitmq: host: 127.0.0.1 virtual-host: / port: 5672 username: guest password: guest weixin: appid: #公众账号ID partner: #商户号 partnerkey: #商户密钥 notifyurl: #回调地址 appid: 微信公众账号或开放平台APP的唯一标识 partner:财付通平台的商户账号 partnerkey:财付通平台的商户密钥 notifyurl: 回调地址 8.1需求分析与实现思路

在支付页面上生成支付二维码,并显示订单号和金额

用户拿出手机,打开微信扫描页面上的二维码,然后在微信中完成支付

8.2实现思路

我们通过HttpClient工具类实现对远程支付接口的调用。

接口链接:https://api.mch.weixin.qq.com/pay/unifiedorder

具体参数参见“统一下单”API, 构建参数发送给统一下单的url ,返回的信息中有支付url,根据url生成二维码,显示的订单号和金额也在返回的信息中。

8.3代码实现

控制层:主要调用WeixinPayService的方法获取创建二维码的信息

@RestController @RequestMapping(value = "/weixin/pay") @CrossOrigin public class WeiXinPayController { @Autowired private WeiXinPayService weixinPayService; /*** * 创建二维码 * @return */ @RequestMapping(value = "/create/native") public Dto createNative(String outtradeno, String money){ Map resultMap = weixinPayService.createNative(outtradeno,money); return DtoUtil.returnSuccess("创建二维码预付订单成功!",resultMap); } }

业务层:

@Service public class WeiXinPayServiceImpl implements WeiXinPayService { @Value("${weixin.appid}") private String appid; @Value("${weixin.partner}") private String partner; @Value("${weixin.partnerkey}") private String partnerkey; @Value("${weixin.notifyurl}") private String notifyurl; @Override public Map createNative(String trade_no, String total_lee) { try { //1、封装参数 Map param = new HashMap(); param.put("appid", appid); //应用ID param.put("mch_id", partner); //商户ID号 param.put("nonce_str", WXPayUtil.generateNonceStr()); //随机数 param.put("body", "My商场"); //订单描述 param.put("out_trade_no",trade_no); //商户订单号 param.put("total_fee", total_lee); //交易金额 param.put("spbill_create_ip", "127.0.0.1"); //终端IP param.put("notify_url", notifyurl); //回调地址 param.put("trade_type", "NATIVE"); //交易类型 //2、将参数转成xml字符,并携带签名 String paramXml = WXPayUtil.generateSignedXml(param,partnerkey); ///3、执行请求 HttpClient httpClient = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder"); httpClient.setHttps(true); httpClient.setXmlParam(paramXml); httpClient.post(); //4、获取参数 String content = httpClient.getContent(); Map stringMap = WXPayUtil.xmlToMap(content); System.out.println("stringMap:"+stringMap); //5、获取部分页面所需参数 Map dataMap = new HashMap(); dataMap.put("code_url",stringMap.get("code_url")); dataMap.put("out_trade_no",trade_no); dataMap.put("total_fee",total_lee); return dataMap; } catch (Exception e) { e.printStackTrace(); } return null; } }

这里我们订单号通过随机数生成,金额暂时写死,后续开发我们再对接业务系统得到订单号和金额

Postman测试

http://localhost:7008/weixin/pay/create/native?outtradeno=10004&money=1

打开支付页面/pay.html,修改value路径,然后打开,会出现二维码,可以扫码

测试如下:

8.4 查询订单 8.4.1实现思路

我们通过HttpClient工具类实现对远程支付接口的调用。

接口链接:https://api.mch.weixin.qq.com/pay/orderquery

具体参数参见“查询订单”API, 我们在controller方法中轮询调用查询订单(间隔3秒),当返回状态为success时,我们会在controller方法返回结果。前端代码收到结果后跳转到成功页面。

8.4.2代码实现

业务层:

Override public Map queryPayStatus(String out_trade_no) { try { //1、封装参数 Map param = new HashMap(); param.put("appid", appid); //应用ID param.put("mch_id", partner); //商户ID号 param.put("nonce_str", WXPayUtil.generateNonceStr()); //随机数 param.put("out_trade_no",out_trade_no); //商户订单号 //2、将参数转成xml字符,并携带签名 String paramXml = WXPayUtil.generateSignedXml(param,partnerkey); ///3、执行请求 HttpClient httpClient = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery"); httpClient.setHttps(true); httpClient.setXmlParam(paramXml); httpClient.post(); //4、获取参数 String content = httpClient.getContent(); Map stringMap = WXPayUtil.xmlToMap(content); return stringMap; } catch (Exception e) { e.printStackTrace(); } return null; }

控制层:用于查询支付状态,代码如下

/*** * 查询支付状态 * @param outtradeno * @return */ @GetMapping(value = "/status/query") public Dto queryStatus(String outtradeno){ Map resultMap = weixinPayService.queryPayStatus(outtradeno); return DtoUtil.returnSuccess("查询状态成功!",resultMap); }

测试

http://zhangjian.free.idcfengye.com/weixin/pay/status/query?outtradeno=10002

8.5 支付信息回调 8.5.1接口分析

每次实现支付之后,微信支付都会将用户支付结果返回到指定路径,而指定路径是指创建二维码的时候填写的notifyurl参数,响应的数据以及相关文档参考一下地址:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_7&index=8

8.5.2前提回调方法的接口不能直接写入本地IP地址,微信回调会找不到我们的回调接口 (如果想要被微信回调我们需要进行内网穿透)

8.5.3内网穿透

地址:https://natapp.cn/

8.5.3.1开通:

8.5.3.2下载客户端工具:

8.5.3.3购买隧道

8.5.3.4修改端口

8.5.3.5购买成功:

8.5.3.6运行客户端:

8.5.3.7获取IP地址:

8.5.3.8获取IP地址:

8.5.4回调接收数据实现

添加回调方法,代码如下:

@RequestMapping(value = "/notify/url") public String notifyUrl(HttpServletRequest request){ InputStream inStream; try { //读取支付回调数据 inStream = request.getInputStream(); ByteArrayOutputStream outSteam = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len = 0; while ((len = inStream.read(buffer)) != -1) { outSteam.write(buffer, 0, len); } outSteam.close(); inStream.close(); // 将支付回调数据转换成xml字符串 String result = new String(outSteam.toByteArray(), "utf-8"); //将xml字符串转换成Map结构 Map map = WXPayUtil.xmlToMap(result); System.out.println("获取接收到的数据;"+map); //响应数据设置 Map respMap = new HashMap(); respMap.put("return_code","SUCCESS"); respMap.put("return_msg","OK"); return WXPayUtil.mapToXml(respMap); } catch (Exception e) { e.printStackTrace(); //记录错误日志 } return null; }

直接扫描支付,的回调方法自动执行



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有